/*
 * contrib/btree_gist/btree_utils_num.c
 */
#include "postgres.h"

#include "btree_gist.h"
#include "btree_utils_num.h"
#include "utils/cash.h"
#include "utils/date.h"
#include "utils/timestamp.h"


GISTENTRY * gbt_num_compress(GISTENTRY *fc_entry, const gbtree_ninfo *fc_tinfo)
{
	GISTENTRY  *fc_retval;

	if (fc_entry->leafkey)
	{
		union
		{
			bool		bo;
			int16		i2;
			int32		i4;
			int64		i8;
			float4		f4;
			float8		f8;
			DateADT		dt;
			TimeADT		tm;
			Timestamp	ts;
			Cash		ch;
		}			fc_v;

		GBT_NUMKEY *fc_r = (GBT_NUMKEY *) palloc0(fc_tinfo->indexsize);
		void	   *fc_leaf = NULL;

		switch (fc_tinfo->t)
		{
			case gbt_t_bool:
				fc_v.bo = DatumGetBool(fc_entry->key);
				fc_leaf = &fc_v.bo;
				break;
			case gbt_t_int2:
				fc_v.i2 = DatumGetInt16(fc_entry->key);
				fc_leaf = &fc_v.i2;
				break;
			case gbt_t_int4:
				fc_v.i4 = DatumGetInt32(fc_entry->key);
				fc_leaf = &fc_v.i4;
				break;
			case gbt_t_int8:
				fc_v.i8 = DatumGetInt64(fc_entry->key);
				fc_leaf = &fc_v.i8;
				break;
			case gbt_t_oid:
			case gbt_t_enum:
				fc_v.i4 = DatumGetObjectId(fc_entry->key);
				fc_leaf = &fc_v.i4;
				break;
			case gbt_t_float4:
				fc_v.f4 = DatumGetFloat4(fc_entry->key);
				fc_leaf = &fc_v.f4;
				break;
			case gbt_t_float8:
				fc_v.f8 = DatumGetFloat8(fc_entry->key);
				fc_leaf = &fc_v.f8;
				break;
			case gbt_t_date:
				fc_v.dt = DatumGetDateADT(fc_entry->key);
				fc_leaf = &fc_v.dt;
				break;
			case gbt_t_time:
				fc_v.tm = DatumGetTimeADT(fc_entry->key);
				fc_leaf = &fc_v.tm;
				break;
			case gbt_t_ts:
				fc_v.ts = DatumGetTimestamp(fc_entry->key);
				fc_leaf = &fc_v.ts;
				break;
			case gbt_t_cash:
				fc_v.ch = DatumGetCash(fc_entry->key);
				fc_leaf = &fc_v.ch;
				break;
			default:
				fc_leaf = DatumGetPointer(fc_entry->key);
		}

		Assert(fc_tinfo->indexsize >= 2 * fc_tinfo->size);

		memcpy((void *) &fc_r[0], fc_leaf, fc_tinfo->size);
		memcpy((void *) &fc_r[fc_tinfo->size], fc_leaf, fc_tinfo->size);
		fc_retval = palloc(sizeof(GISTENTRY));
		gistentryinit(*fc_retval, PointerGetDatum(fc_r), fc_entry->rel, fc_entry->page,
					  fc_entry->offset, false);
	}
	else
		fc_retval = fc_entry;

	return fc_retval;
}

/*
 * 将压缩的叶子项转换回原始类型，用于仅索引扫描。
 */
GISTENTRY * gbt_num_fetch(GISTENTRY *fc_entry, const gbtree_ninfo *fc_tinfo)
{
	GISTENTRY  *fc_retval;
	Datum		fc_datum;

	Assert(fc_tinfo->indexsize >= 2 * fc_tinfo->size);

	/*
	 * 从存储的datum中获取原始Datum。在叶子条目中，下限和上限是相同的。我们只需获取下限并返回它。
	 */
	switch (fc_tinfo->t)
	{
		case gbt_t_bool:
			fc_datum = BoolGetDatum(*(bool *) fc_entry->key);
			break;
		case gbt_t_int2:
			fc_datum = Int16GetDatum(*(int16 *) fc_entry->key);
			break;
		case gbt_t_int4:
			fc_datum = Int32GetDatum(*(int32 *) fc_entry->key);
			break;
		case gbt_t_int8:
			fc_datum = Int64GetDatum(*(int64 *) fc_entry->key);
			break;
		case gbt_t_oid:
		case gbt_t_enum:
			fc_datum = ObjectIdGetDatum(*(Oid *) fc_entry->key);
			break;
		case gbt_t_float4:
			fc_datum = Float4GetDatum(*(float4 *) fc_entry->key);
			break;
		case gbt_t_float8:
			fc_datum = Float8GetDatum(*(float8 *) fc_entry->key);
			break;
		case gbt_t_date:
			fc_datum = DateADTGetDatum(*(DateADT *) fc_entry->key);
			break;
		case gbt_t_time:
			fc_datum = TimeADTGetDatum(*(TimeADT *) fc_entry->key);
			break;
		case gbt_t_ts:
			fc_datum = TimestampGetDatum(*(Timestamp *) fc_entry->key);
			break;
		case gbt_t_cash:
			fc_datum = CashGetDatum(*(Cash *) fc_entry->key);
			break;
		default:
			fc_datum = PointerGetDatum(fc_entry->key);
	}

	fc_retval = palloc(sizeof(GISTENTRY));
	gistentryinit(*fc_retval, fc_datum, fc_entry->rel, fc_entry->page, fc_entry->offset,
				  false);
	return fc_retval;
}



/*
** GiST数值的并集方法
*/

void * gbt_num_union(GBT_NUMKEY *fc_out, const GistEntryVector *fc_entryvec, const gbtree_ninfo *fc_tinfo, FmgrInfo *fc_flinfo)
{
	int			fc_i,
				fc_numranges;
	GBT_NUMKEY *fc_cur;
	GBT_NUMKEY_R fc_o,
				fc_c;

	fc_numranges = fc_entryvec->n;
	fc_cur = (GBT_NUMKEY *) DatumGetPointer((fc_entryvec->vector[0].key));


	fc_o.lower = &((GBT_NUMKEY *) fc_out)[0];
	fc_o.upper = &((GBT_NUMKEY *) fc_out)[fc_tinfo->size];

	memcpy((void *) fc_out, (void *) fc_cur, 2 * fc_tinfo->size);

	for (fc_i = 1; fc_i < fc_numranges; fc_i++)
	{
		fc_cur = (GBT_NUMKEY *) DatumGetPointer((fc_entryvec->vector[fc_i].key));
		fc_c.lower = &fc_cur[0];
		fc_c.upper = &fc_cur[fc_tinfo->size];
		/* 如果out->lower > cur->lower，则采用cur作为下限 */
		if (fc_tinfo->f_gt(fc_o.lower, fc_c.lower, fc_flinfo))
			memcpy(unconstify(GBT_NUMKEY *, fc_o.lower), fc_c.lower, fc_tinfo->size);
		/* 如果out->upper < cur->upper，则采用cur作为上限 */
		if (fc_tinfo->f_lt(fc_o.upper, fc_c.upper, fc_flinfo))
			memcpy(unconstify(GBT_NUMKEY *, fc_o.upper), fc_c.upper, fc_tinfo->size);
	}

	return fc_out;
}



/*
** GiST数值的同一方法
*/

bool gbt_num_same(const GBT_NUMKEY *fc_a, const GBT_NUMKEY *fc_b, const gbtree_ninfo *fc_tinfo, FmgrInfo *fc_flinfo)
{
	GBT_NUMKEY_R fc_b1,
				fc_b2;

	fc_b1.lower = &(fc_a[0]);
	fc_b1.upper = &(fc_a[fc_tinfo->size]);
	fc_b2.lower = &(fc_b[0]);
	fc_b2.upper = &(fc_b[fc_tinfo->size]);

	return (fc_tinfo->f_eq(fc_b1.lower, fc_b2.lower, fc_flinfo) &&
			fc_tinfo->f_eq(fc_b1.upper, fc_b2.upper, fc_flinfo));
}


void gbt_num_bin_union(Datum *fc_u, GBT_NUMKEY *fc_e, const gbtree_ninfo *fc_tinfo, FmgrInfo *fc_flinfo)
{
	GBT_NUMKEY_R fc_rd;

	fc_rd.lower = &fc_e[0];
	fc_rd.upper = &fc_e[fc_tinfo->size];

	if (!DatumGetPointer(*fc_u))
	{
		*fc_u = PointerGetDatum(palloc0(fc_tinfo->indexsize));
		memcpy(&(((GBT_NUMKEY *) DatumGetPointer(*fc_u))[0]), fc_rd.lower, fc_tinfo->size);
		memcpy(&(((GBT_NUMKEY *) DatumGetPointer(*fc_u))[fc_tinfo->size]), fc_rd.upper, fc_tinfo->size);
	}
	else
	{
		GBT_NUMKEY_R fc_ur;

		fc_ur.lower = &(((GBT_NUMKEY *) DatumGetPointer(*fc_u))[0]);
		fc_ur.upper = &(((GBT_NUMKEY *) DatumGetPointer(*fc_u))[fc_tinfo->size]);
		if (fc_tinfo->f_gt(fc_ur.lower, fc_rd.lower, fc_flinfo))
			memcpy(unconstify(GBT_NUMKEY *, fc_ur.lower), fc_rd.lower, fc_tinfo->size);
		if (fc_tinfo->f_lt(fc_ur.upper, fc_rd.upper, fc_flinfo))
			memcpy(unconstify(GBT_NUMKEY *, fc_ur.upper), fc_rd.upper, fc_tinfo->size);
	}
}



/*
 * GiST一致性方法
 *
 * 注意：我们目前假设使用此例程的没有数据类型是注意排序的；因此我们不需要传递排序信息。
 */
bool gbt_num_consistent(const GBT_NUMKEY_R *fc_key,
				   const void *fc_query,
				   const StrategyNumber *fc_strategy,
				   bool fc_is_leaf,
				   const gbtree_ninfo *fc_tinfo,
				   FmgrInfo *fc_flinfo)
{
	bool		fc_retval;

	switch (*fc_strategy)
	{
		case BTLessEqualStrategyNumber:
			fc_retval = fc_tinfo->f_ge(fc_query, fc_key->lower, fc_flinfo);
			break;
		case BTLessStrategyNumber:
			if (fc_is_leaf)
				fc_retval = fc_tinfo->f_gt(fc_query, fc_key->lower, fc_flinfo);
			else
				fc_retval = fc_tinfo->f_ge(fc_query, fc_key->lower, fc_flinfo);
			break;
		case BTEqualStrategyNumber:
			if (fc_is_leaf)
				fc_retval = fc_tinfo->f_eq(fc_query, fc_key->lower, fc_flinfo);
			else
				fc_retval = (fc_tinfo->f_le(fc_key->lower, fc_query, fc_flinfo) &&
						  fc_tinfo->f_le(fc_query, fc_key->upper, fc_flinfo));
			break;
		case BTGreaterStrategyNumber:
			if (fc_is_leaf)
				fc_retval = fc_tinfo->f_lt(fc_query, fc_key->upper, fc_flinfo);
			else
				fc_retval = fc_tinfo->f_le(fc_query, fc_key->upper, fc_flinfo);
			break;
		case BTGreaterEqualStrategyNumber:
			fc_retval = fc_tinfo->f_le(fc_query, fc_key->upper, fc_flinfo);
			break;
		case BtreeGistNotEqualStrategyNumber:
			fc_retval = (!(fc_tinfo->f_eq(fc_query, fc_key->lower, fc_flinfo) &&
						fc_tinfo->f_eq(fc_query, fc_key->upper, fc_flinfo)));
			break;
		default:
			fc_retval = false;
	}

	return fc_retval;
}


/*
** GiST距离方法（用于KNN-Gist）
*/

float8 gbt_num_distance(const GBT_NUMKEY_R *fc_key,
				 const void *fc_query,
				 bool fc_is_leaf,
				 const gbtree_ninfo *fc_tinfo,
				 FmgrInfo *fc_flinfo)
{
	float8		fc_retval;

	if (fc_tinfo->f_dist == NULL)
		elog(ERROR, "KNN search is not supported for btree_gist type %d",
			 (int) fc_tinfo->t);
	if (fc_tinfo->f_le(fc_query, fc_key->lower, fc_flinfo))
		fc_retval = fc_tinfo->f_dist(fc_query, fc_key->lower, fc_flinfo);
	else if (fc_tinfo->f_ge(fc_query, fc_key->upper, fc_flinfo))
		fc_retval = fc_tinfo->f_dist(fc_query, fc_key->upper, fc_flinfo);
	else
		fc_retval = 0.0;

	return fc_retval;
}


GIST_SPLITVEC *
gbt_num_picksplit(const GistEntryVector *fc_entryvec, GIST_SPLITVEC *fc_v,
				  const gbtree_ninfo *fc_tinfo, FmgrInfo *fc_flinfo)
{
	OffsetNumber fc_i,
				fc_maxoff = fc_entryvec->n - 1;
	Nsrt	   *fc_arr;
	int			fc_nbytes;

	fc_arr = (Nsrt *) palloc((fc_maxoff + 1) * sizeof(Nsrt));
	fc_nbytes = (fc_maxoff + 2) * sizeof(OffsetNumber);
	fc_v->spl_left = (OffsetNumber *) palloc(fc_nbytes);
	fc_v->spl_right = (OffsetNumber *) palloc(fc_nbytes);
	fc_v->spl_ldatum = PointerGetDatum(0);
	fc_v->spl_rdatum = PointerGetDatum(0);
	fc_v->spl_nleft = 0;
	fc_v->spl_nright = 0;

	/* 排序条目 */

	for (fc_i = FirstOffsetNumber; fc_i <= fc_maxoff; fc_i = OffsetNumberNext(fc_i))
	{
		fc_arr[fc_i].t = (GBT_NUMKEY *) DatumGetPointer((fc_entryvec->vector[fc_i].key));
		fc_arr[fc_i].i = fc_i;
	}
	qsort_arg((void *) &fc_arr[FirstOffsetNumber], fc_maxoff - FirstOffsetNumber + 1, sizeof(Nsrt), (qsort_arg_comparator) fc_tinfo->f_cmp, (void *) fc_flinfo);

	/* 我们确实简单地创建两个部分 */

	for (fc_i = FirstOffsetNumber; fc_i <= fc_maxoff; fc_i = OffsetNumberNext(fc_i))
	{
		if (fc_i <= (fc_maxoff - FirstOffsetNumber + 1) / 2)
		{
			gbt_num_bin_union(&fc_v->spl_ldatum, fc_arr[fc_i].t, fc_tinfo, fc_flinfo);
			fc_v->spl_left[fc_v->spl_nleft] = fc_arr[fc_i].i;
			fc_v->spl_nleft++;
		}
		else
		{
			gbt_num_bin_union(&fc_v->spl_rdatum, fc_arr[fc_i].t, fc_tinfo, fc_flinfo);
			fc_v->spl_right[fc_v->spl_nright] = fc_arr[fc_i].i;
			fc_v->spl_nright++;
		}
	}

	return fc_v;
}
